Figure 2: Protein complex cohesiveness
# function to get pairwise correlations between complex members
get_corr <- function(x, subset="none", shared = FALSE) {
male_samples <- covarTidy.esc_prot %>% filter( sex =="M")
female_samples <-covarTidy.esc_prot %>% filter( sex =="F")
if( subset == "M"){
expr_prot <- expr.esc_prot[intersect(shared.samples, male_samples$sampleid),all.prots$protein_id,drop=FALSE ]
expr_rna <- expr.esc_rna[ intersect(shared.samples, male_samples$sampleid),,drop=FALSE ]
}
if( subset == "F"){
expr_prot <- expr.esc_prot[ intersect(shared.samples, female_samples$sampleid),all.prots$protein_id,drop=FALSE ]
expr_rna <- expr.esc_rna[ intersect(shared.samples, female_samples$sampleid),,drop=FALSE ]
}
if( subset =="none"){
expr_prot <- expr.esc_prot[ shared.samples,all.prots$protein_id,drop=FALSE ]
expr_rna <- expr.esc_rna[ shared.samples,,drop=FALSE ]
}
ids <- filter(complex.gene.list, human_ids %in% unlist(x))
if ( shared ==T & length(intersect(shared.genes$ensembl_gene_id, ids$ensembl_gene_id)) > 1 &
length(intersect(all.prots$protein_id, ids$protein_id)) > 1 ) {
cors.rna <- rcorr(expr_rna[, unique(intersect(shared.genes$ensembl_gene_id, ids$ensembl_gene_id)),drop=FALSE])
cors.rna.df <- tibble( col_rna = colnames(cors.rna$r)[col(cors.rna$r)] ,
row_rna = rownames(cors.rna$r)[row(cors.rna$r)] ,
cor_rna = c(cors.rna$r),
n_rna = c(cors.rna$n),
p_rna = c(cors.rna$P)) %>%
left_join( .,
select(all.genes,
ensembl_gene_id, mgi_symbol),
by=c("col_rna"="ensembl_gene_id")) %>%
rename( ensembl_gene_id_col = col_rna,
mgi_symbol_col = mgi_symbol) %>%
left_join(.,
select(all.genes,
ensembl_gene_id, mgi_symbol),
by=c("row_rna"="ensembl_gene_id")) %>%
rename(ensembl_gene_id_row = row_rna,
mgi_symbol_row = mgi_symbol)
cors.prot <- rcorr(expr_prot[, unique(intersect(shared.genes$protein_id, ids$protein_id)),drop=FALSE])
cors.prot.df <- tibble( col_prot = colnames(cors.prot$r)[col(cors.prot$r)] ,
row_prot = rownames(cors.prot$r)[row(cors.prot$r)] ,
cor_prot = c(cors.prot$r),
n_prot = c(cors.prot$n),
p_prot = c(cors.prot$P)) %>%
left_join( .,
select(all.prots2,
protein_id, ensembl_gene_id, mgi_symbol),
by=c("col_prot"="protein_id")) %>%
rename( protein_id_col = col_prot ,
mgi_symbol_col = mgi_symbol) %>%
left_join( .,
select(all.prots2,
ensembl_gene_id, protein_id, mgi_symbol),
by=c("row_prot"="protein_id")) %>%
rename(protein_id_row = row_prot,
mgi_symbol_row = mgi_symbol)
all.cors <- full_join( cors.prot.df, cors.rna.df) %>%
mutate( diff = cor_prot - cor_rna)
return(all.cors)
}
if(shared ==F & length(intersect(all.prots$protein_id, ids$protein_id)) > 1){
cors.prot <- rcorr(expr_prot[, unique(intersect(shared.genes$protein_id, ids$protein_id)),drop=FALSE])
cors.prot.df <- tibble( col_prot = colnames(cors.prot$r)[col(cors.prot$r)] ,
row_prot = rownames(cors.prot$r)[row(cors.prot$r)] ,
cor_prot = c(cors.prot$r),
n_prot = c(cors.prot$n),
p_prot = c(cors.prot$P)) %>%
left_join( .,
select(all.prots2,
protein_id, ensembl_gene_id, mgi_symbol),
by=c("col_prot"="protein_id")) %>%
rename( protein_id_col = col_prot ,
ensembl_gene_id_col = ensembl_gene_id,
mgi_symbol_col = mgi_symbol) %>%
left_join( .,
select(all.prots2,
ensembl_gene_id, protein_id, mgi_symbol),
by=c("row_prot"="protein_id")) %>%
rename(protein_id_row = row_prot,
ensembl_gene_id_row = ensembl_gene_id,
mgi_symbol_row = mgi_symbol)
return(cors.prot.df)
}
return(NA)
}
# let's get correlations for all the complexes then filter later
names(complex.genes) <- complexes$`Complex Name`
complex_prot_cor <- complex.genes %>%
map( get_corr, shared = FALSE)
complex_genes <- complex.genes %>%
enframe( "Complex Name","human_ids") %>%
unnest(human_ids) %>%
left_join( complex.gene.list) %>%
filter( !is.na(protein_id)) %>%
group_by(ensembl_gene_id, protein_id, `Complex Name`) %>% mutate( n = seq(1:n())) %>% filter( n == 1) %>% select(-n) %>% ungroup() %>% #there are some human ids that match to the same gene/protein, we need to clean those up before doing means etc.
filter( !protein_id %in% (filter(peaks.esc_prot, lod > 7.5))$phenotype ) %>% # filter proteins with significant pQTL
group_by(`Complex Name`) %>%
mutate( n_complex = n_distinct(protein_id)) %>%
ungroup() %>%
filter( n_complex > 4) %>% # filter complexes <5
group_by(protein_id) %>%
mutate( n_overlap = n_distinct(`Complex Name`)) %>% # add the overlap of proteins ONLY for the complexes we have in our analysis.
ungroup() %>%
group_by(`Complex Name`) %>%
mutate(n_mean = mean(n_overlap)) %>%
ungroup()
#remaining complexes to analyze
complexes_to_analyze <- complex_genes %>%
select( `Complex Name`, n_complex, n_mean) %>%
distinct()
complex_prot_cor_df <- complex_prot_cor %>%
enframe( "Complex Name", "data") %>%
unnest("data") %>%
filter( !is.na(protein_id_col), !is.na(protein_id_row) ) %>% # filter NAs if any.
filter( `Complex Name` %in% complexes_to_analyze$`Complex Name`) %>% # filter the complexes
filter( protein_id_col %in% complex_genes$protein_id,
protein_id_row %in% complex_genes$protein_id) # filter for the genes to analyze
complex_prot_cor_df %>%
filter( protein_id_col != protein_id_row) %>%
group_by(`Complex Name`) %>%
summarise( median_prot = median(cor_prot, na.rm=TRUE)
) %>%
ungroup() -> mean_complex_prot
complex_prot_cor_df %>%
filter( protein_id_col != protein_id_row) %>%
group_by(`Complex Name`,protein_id_col) %>%
summarize( cor_prot = median(cor_prot, na.rm=T)) %>%
rename(protein_id = protein_id_col) %>%
ungroup() -> mean_gene_complex_prot
complex_genes %>%
left_join( .,
rename(mean_gene_complex_prot,
cor_gene_prot = cor_prot) ) %>%
left_join(.,
rename(mean_complex_prot,
cor_complex_prot = median_prot))-> complex_genes_annotated
complexes_annotated <- mean_complex_prot %>%
mutate( complex_q75 = quantile( (median_prot), 0.90) ,
complex_q25 = quantile( (median_prot), 0.1) ) %>%
mutate( complex_type = case_when( median_prot > complex_q75 ~ "stable",
median_prot < complex_q25 ~ "variable",
(median_prot >= complex_q25 & median_prot <= complex_q75) ~ "none")
) %>%
select( `Complex Name`,complex_type)%>%
left_join( select( complex_genes_annotated,
`Complex Name`,
cor_complex_prot,
n_complex,
n_mean))
Figure2_data <- complex_genes_annotated %>%
left_join( select(complexes_annotated, `Complex Name`, complex_type)) %>%
distinct() %>%
mutate( mgi_symbol = toupper(mgi_symbol)) %>%
select( `Complex Name`,
`Complex Cohesivenes` = cor_complex_prot,
`Protein` = mgi_symbol,
`Average pairwise correlation` = cor_gene_prot) %>%
distinct()
Figure2_data %>%
arrange( desc(`Complex Cohesivenes`) ) %>%
mutate( label = factor(`Complex Name`, levels = unique(`Complex Name`))) %>%
ggboxplot(
x = "label",
y = "Average pairwise correlation",
fill = "Complex Cohesivenes",
sort.val = "desc",
xlab = "",
ylab = "Correlation",
width = 1
)+
theme_pubclean( base_size = 10, base_family ="poppins")+
theme(axis.text.x = element_text(angle = 0, hjust = 1, size = 0),
legend.position = "top",
legend.text = element_text(angle=15, hjust =0.1, size = 12),
legend.title = element_text(angle=0, hjust =0, vjust = 0.9, size = 16),
legend.title.align = 1,
axis.text.y = element_text(angle=0, size = 18),
axis.title.y = element_text(angle=90, size = 18),
axis.ticks.x = element_blank())+
labs( fill = "Complex\ncohesiveness")+
scale_fill_viridis_c()
Figure2_data %>%
mutate_if( is.numeric, round ,2 ) %>%
create_dt()
LS0tCnRpdGxlOiAiQ28tdmFyaWF0aW9uIGluIHByb3RlaW4gY29tcGxleCBtZW1iZXJzIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2RlcHRoOiA0CiAgICB0b2NfZmxvYXQ6IAogICAgICBjb2xsYXBzZWQ6IGZhbHNlCiAgICAgIHNtb290aF9zY3JvbGw6IGZhbHNlCiAgICBkZl9wcmludDogcGFnZWQKICAgIGNvZGVfZm9sZGluZzogaGlkZQotLS0KCjxzdHlsZT4KcC5jYXB0aW9uIHsKICBmb250LXNpemU6IDFlbTsKfQo8L3N0eWxlPgoKCgpgYGB7ciBzZXR1cH0KCiMgb3B0aW9ucwpvcHRpb25zKHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFKQprbml0cjo6b3B0c19rbml0JHNldChwcm9ncmVzcyA9IEZBTFNFKQoKYGBgCgo8YnI+Cjxicj4KCiMjIyBGaWd1cmUgMjogUHJvdGVpbiBjb21wbGV4IGNvaGVzaXZlbmVzcwoKYGBge3IgY29tcGxleF9jb3JfcGxvdCwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgY2FjaGU9VFJVRSwgcmVzdWx0cz0naGlkZSd9CgojIGZ1bmN0aW9uIHRvIGdldCBwYWlyd2lzZSBjb3JyZWxhdGlvbnMgYmV0d2VlbiBjb21wbGV4IG1lbWJlcnMKZ2V0X2NvcnIgPC0gZnVuY3Rpb24oeCwgc3Vic2V0PSJub25lIiwgc2hhcmVkID0gRkFMU0UpIHsKICAKICBtYWxlX3NhbXBsZXMgPC0gIGNvdmFyVGlkeS5lc2NfcHJvdCAlPiUgIGZpbHRlciggc2V4ID09Ik0iKSAKICBmZW1hbGVfc2FtcGxlcyA8LWNvdmFyVGlkeS5lc2NfcHJvdCAlPiUgIGZpbHRlciggc2V4ID09IkYiKSAKICAKICBpZiggc3Vic2V0ID09ICJNIil7CiAgICBleHByX3Byb3QgPC0gZXhwci5lc2NfcHJvdFtpbnRlcnNlY3Qoc2hhcmVkLnNhbXBsZXMsIG1hbGVfc2FtcGxlcyRzYW1wbGVpZCksYWxsLnByb3RzJHByb3RlaW5faWQsZHJvcD1GQUxTRSBdCiAgICBleHByX3JuYSAgPC0gZXhwci5lc2Nfcm5hWyBpbnRlcnNlY3Qoc2hhcmVkLnNhbXBsZXMsIG1hbGVfc2FtcGxlcyRzYW1wbGVpZCksLGRyb3A9RkFMU0UgXQogIH0KICBpZiggc3Vic2V0ID09ICJGIil7CiAgICBleHByX3Byb3QgPC0gZXhwci5lc2NfcHJvdFsgaW50ZXJzZWN0KHNoYXJlZC5zYW1wbGVzLCBmZW1hbGVfc2FtcGxlcyRzYW1wbGVpZCksYWxsLnByb3RzJHByb3RlaW5faWQsZHJvcD1GQUxTRSBdCiAgICBleHByX3JuYSAgPC0gZXhwci5lc2Nfcm5hWyBpbnRlcnNlY3Qoc2hhcmVkLnNhbXBsZXMsIGZlbWFsZV9zYW1wbGVzJHNhbXBsZWlkKSwsZHJvcD1GQUxTRSBdCiAgfQogIGlmKCBzdWJzZXQgPT0ibm9uZSIpewogICAgZXhwcl9wcm90IDwtIGV4cHIuZXNjX3Byb3RbIHNoYXJlZC5zYW1wbGVzLGFsbC5wcm90cyRwcm90ZWluX2lkLGRyb3A9RkFMU0UgXQogICAgZXhwcl9ybmEgIDwtIGV4cHIuZXNjX3JuYVsgc2hhcmVkLnNhbXBsZXMsLGRyb3A9RkFMU0UgXQogIH0KICAgIAogIAogIGlkcyA8LSBmaWx0ZXIoY29tcGxleC5nZW5lLmxpc3QsIGh1bWFuX2lkcyAlaW4lIHVubGlzdCh4KSkKICBpZiAoIHNoYXJlZCA9PVQgJiBsZW5ndGgoaW50ZXJzZWN0KHNoYXJlZC5nZW5lcyRlbnNlbWJsX2dlbmVfaWQsIGlkcyRlbnNlbWJsX2dlbmVfaWQpKSA+IDEgJgogICAgICAgICBsZW5ndGgoaW50ZXJzZWN0KGFsbC5wcm90cyRwcm90ZWluX2lkLCBpZHMkcHJvdGVpbl9pZCkpID4gMSApIHsKICAgICAgIAogICAgICAgIGNvcnMucm5hIDwtIHJjb3JyKGV4cHJfcm5hWywgdW5pcXVlKGludGVyc2VjdChzaGFyZWQuZ2VuZXMkZW5zZW1ibF9nZW5lX2lkLCBpZHMkZW5zZW1ibF9nZW5lX2lkKSksZHJvcD1GQUxTRV0pCiAgICAgICAgY29ycy5ybmEuZGYgPC0gdGliYmxlKCBjb2xfcm5hID0gY29sbmFtZXMoY29ycy5ybmEkcilbY29sKGNvcnMucm5hJHIpXSAsCiAgICAgICAgICAgICAgICAgICAgICAgICByb3dfcm5hID0gcm93bmFtZXMoY29ycy5ybmEkcilbcm93KGNvcnMucm5hJHIpXSAsCiAgICAgICAgICAgICAgICAgICAgICAgICBjb3Jfcm5hID0gYyhjb3JzLnJuYSRyKSwKICAgICAgICAgICAgICAgICAgICAgICAgIG5fcm5hID0gYyhjb3JzLnJuYSRuKSwKICAgICAgICAgICAgICAgICAgICAgICAgIHBfcm5hID0gYyhjb3JzLnJuYSRQKSkgJT4lIAogICAgICAgICAgbGVmdF9qb2luKCAuLAogICAgICAgICAgICAgICAgICAgICBzZWxlY3QoYWxsLmdlbmVzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVuc2VtYmxfZ2VuZV9pZCwgbWdpX3N5bWJvbCksIAogICAgICAgICAgICAgICAgICAgICBieT1jKCJjb2xfcm5hIj0iZW5zZW1ibF9nZW5lX2lkIikpICU+JSAKICAgICAgICAgIHJlbmFtZSggZW5zZW1ibF9nZW5lX2lkX2NvbCA9IGNvbF9ybmEsCiAgICAgICAgICAgICAgICAgIG1naV9zeW1ib2xfY29sID0gbWdpX3N5bWJvbCkgJT4lIAogICAgICAgICAgbGVmdF9qb2luKC4sCiAgICAgICAgICAgICAgICAgICAgc2VsZWN0KGFsbC5nZW5lcywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGVuc2VtYmxfZ2VuZV9pZCwgbWdpX3N5bWJvbCksIAogICAgICAgICAgICAgICAgICAgIGJ5PWMoInJvd19ybmEiPSJlbnNlbWJsX2dlbmVfaWQiKSkgJT4lIAogICAgICAgICAgcmVuYW1lKGVuc2VtYmxfZ2VuZV9pZF9yb3cgPSByb3dfcm5hLAogICAgICAgICAgICAgICAgIG1naV9zeW1ib2xfcm93ID0gbWdpX3N5bWJvbCkgCgogICAgICAgIGNvcnMucHJvdCA8LSByY29ycihleHByX3Byb3RbLCB1bmlxdWUoaW50ZXJzZWN0KHNoYXJlZC5nZW5lcyRwcm90ZWluX2lkLCBpZHMkcHJvdGVpbl9pZCkpLGRyb3A9RkFMU0VdKQogICAgCiAgICAgICAgY29ycy5wcm90LmRmIDwtIHRpYmJsZSggY29sX3Byb3QgPSBjb2xuYW1lcyhjb3JzLnByb3QkcilbY29sKGNvcnMucHJvdCRyKV0gLAogICAgICAgICAgICAgICAgICAgICAgICAgcm93X3Byb3QgPSByb3duYW1lcyhjb3JzLnByb3Qkcilbcm93KGNvcnMucHJvdCRyKV0gLAogICAgICAgICAgICAgICAgICAgICAgICAgY29yX3Byb3QgPSBjKGNvcnMucHJvdCRyKSwKICAgICAgICAgICAgICAgICAgICAgICAgIG5fcHJvdCA9IGMoY29ycy5wcm90JG4pLAogICAgICAgICAgICAgICAgICAgICAgICAgcF9wcm90ID0gYyhjb3JzLnByb3QkUCkpICU+JSAKICAgICAgICAgIGxlZnRfam9pbiggLiwKICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KGFsbC5wcm90czIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvdGVpbl9pZCwgZW5zZW1ibF9nZW5lX2lkLCBtZ2lfc3ltYm9sKSwgCiAgICAgICAgICAgICAgICAgICAgIGJ5PWMoImNvbF9wcm90Ij0icHJvdGVpbl9pZCIpKSAlPiUgCiAgICAgICAgICByZW5hbWUoIHByb3RlaW5faWRfY29sID0gY29sX3Byb3QgLAogICAgICAgICAgICAgICAgICBtZ2lfc3ltYm9sX2NvbCA9IG1naV9zeW1ib2wpICU+JSAKICAgICAgICAgIGxlZnRfam9pbiggLiwKICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KGFsbC5wcm90czIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgZW5zZW1ibF9nZW5lX2lkLCBwcm90ZWluX2lkLCBtZ2lfc3ltYm9sKSwgCiAgICAgICAgICAgICAgICAgICAgIGJ5PWMoInJvd19wcm90Ij0icHJvdGVpbl9pZCIpKSAlPiUgCiAgICAgICAgICByZW5hbWUocHJvdGVpbl9pZF9yb3cgPSByb3dfcHJvdCwKICAgICAgICAgICAgICAgICBtZ2lfc3ltYm9sX3JvdyA9IG1naV9zeW1ib2wpIAogICAgICAgIAogICAgICAgIGFsbC5jb3JzIDwtIGZ1bGxfam9pbiggY29ycy5wcm90LmRmLCBjb3JzLnJuYS5kZikgJT4lIAogICAgICAgICAgbXV0YXRlKCBkaWZmID0gY29yX3Byb3QgLSBjb3Jfcm5hKSAKICAgICAgICByZXR1cm4oYWxsLmNvcnMpCiAgfQogIGlmKHNoYXJlZCA9PUYgJiBsZW5ndGgoaW50ZXJzZWN0KGFsbC5wcm90cyRwcm90ZWluX2lkLCBpZHMkcHJvdGVpbl9pZCkpID4gMSl7CiAgICAKICAgICAgICAgY29ycy5wcm90IDwtIHJjb3JyKGV4cHJfcHJvdFssIHVuaXF1ZShpbnRlcnNlY3Qoc2hhcmVkLmdlbmVzJHByb3RlaW5faWQsIGlkcyRwcm90ZWluX2lkKSksZHJvcD1GQUxTRV0pCiAgICAKICAgICAgICAgY29ycy5wcm90LmRmIDwtIHRpYmJsZSggY29sX3Byb3QgPSBjb2xuYW1lcyhjb3JzLnByb3QkcilbY29sKGNvcnMucHJvdCRyKV0gLAogICAgICAgICAgICAgICAgICAgICAgICAgcm93X3Byb3QgPSByb3duYW1lcyhjb3JzLnByb3Qkcilbcm93KGNvcnMucHJvdCRyKV0gLAogICAgICAgICAgICAgICAgICAgICAgICAgY29yX3Byb3QgPSBjKGNvcnMucHJvdCRyKSwKICAgICAgICAgICAgICAgICAgICAgICAgIG5fcHJvdCA9IGMoY29ycy5wcm90JG4pLAogICAgICAgICAgICAgICAgICAgICAgICAgcF9wcm90ID0gYyhjb3JzLnByb3QkUCkpICU+JSAKICAgICAgICAgIGxlZnRfam9pbiggLiwKICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KGFsbC5wcm90czIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvdGVpbl9pZCwgZW5zZW1ibF9nZW5lX2lkLCBtZ2lfc3ltYm9sKSwgCiAgICAgICAgICAgICAgICAgICAgIGJ5PWMoImNvbF9wcm90Ij0icHJvdGVpbl9pZCIpKSAlPiUgCiAgICAgICAgICByZW5hbWUoIHByb3RlaW5faWRfY29sID0gY29sX3Byb3QgLAogICAgICAgICAgICAgICAgICBlbnNlbWJsX2dlbmVfaWRfY29sID0gZW5zZW1ibF9nZW5lX2lkLAogICAgICAgICAgICAgICAgICBtZ2lfc3ltYm9sX2NvbCA9IG1naV9zeW1ib2wpICU+JSAKICAgICAgICAgIGxlZnRfam9pbiggLiwKICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KGFsbC5wcm90czIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgZW5zZW1ibF9nZW5lX2lkLCBwcm90ZWluX2lkLCBtZ2lfc3ltYm9sKSwgCiAgICAgICAgICAgICAgICAgICAgIGJ5PWMoInJvd19wcm90Ij0icHJvdGVpbl9pZCIpKSAlPiUgCiAgICAgICAgICByZW5hbWUocHJvdGVpbl9pZF9yb3cgPSByb3dfcHJvdCwKICAgICAgICAgICAgICAgICBlbnNlbWJsX2dlbmVfaWRfcm93ID0gZW5zZW1ibF9nZW5lX2lkLAogICAgICAgICAgICAgICAgIG1naV9zeW1ib2xfcm93ID0gbWdpX3N5bWJvbCkgCiAgICAgICAgCiAgICAgICAgIHJldHVybihjb3JzLnByb3QuZGYpCiAgfQogIHJldHVybihOQSkKICAKfQoKIyBsZXQncyBnZXQgY29ycmVsYXRpb25zIGZvciBhbGwgdGhlIGNvbXBsZXhlcyB0aGVuIGZpbHRlciBsYXRlcgpuYW1lcyhjb21wbGV4LmdlbmVzKSA8LSBjb21wbGV4ZXMkYENvbXBsZXggTmFtZWAKY29tcGxleF9wcm90X2NvciA8LSBjb21wbGV4LmdlbmVzICU+JQogIG1hcCggZ2V0X2NvcnIsIHNoYXJlZCA9IEZBTFNFKSAKCmNvbXBsZXhfZ2VuZXMgPC0gY29tcGxleC5nZW5lcyAlPiUgIAogIGVuZnJhbWUoICJDb21wbGV4IE5hbWUiLCJodW1hbl9pZHMiKSAlPiUKICB1bm5lc3QoaHVtYW5faWRzKSAlPiUgCiAgbGVmdF9qb2luKCBjb21wbGV4LmdlbmUubGlzdCkgJT4lIAogIGZpbHRlciggIWlzLm5hKHByb3RlaW5faWQpKSAlPiUgCiAgZ3JvdXBfYnkoZW5zZW1ibF9nZW5lX2lkLCBwcm90ZWluX2lkLCBgQ29tcGxleCBOYW1lYCkgJT4lICBtdXRhdGUoIG4gPSBzZXEoMTpuKCkpKSAlPiUgIGZpbHRlciggbiAgPT0gMSkgJT4lICBzZWxlY3QoLW4pICU+JSB1bmdyb3VwKCkgJT4lICN0aGVyZSBhcmUgc29tZSBodW1hbiBpZHMgdGhhdCBtYXRjaCB0byB0aGUgc2FtZSBnZW5lL3Byb3RlaW4sIHdlIG5lZWQgdG8gY2xlYW4gdGhvc2UgdXAgYmVmb3JlIGRvaW5nIG1lYW5zIGV0Yy4KICBmaWx0ZXIoICFwcm90ZWluX2lkICVpbiUgKGZpbHRlcihwZWFrcy5lc2NfcHJvdCwgbG9kID4gNy41KSkkcGhlbm90eXBlICkgJT4lICMgZmlsdGVyIHByb3RlaW5zIHdpdGggc2lnbmlmaWNhbnQgcFFUTAogIGdyb3VwX2J5KGBDb21wbGV4IE5hbWVgKSAlPiUgCiAgbXV0YXRlKCBuX2NvbXBsZXggPSBuX2Rpc3RpbmN0KHByb3RlaW5faWQpKSAlPiUgCiAgdW5ncm91cCgpICU+JSAKICBmaWx0ZXIoIG5fY29tcGxleCA+IDQpICU+JSAjIGZpbHRlciBjb21wbGV4ZXMgPDUgCiAgZ3JvdXBfYnkocHJvdGVpbl9pZCkgJT4lIAogIG11dGF0ZSggbl9vdmVybGFwID0gbl9kaXN0aW5jdChgQ29tcGxleCBOYW1lYCkpICU+JSAjIGFkZCB0aGUgb3ZlcmxhcCBvZiBwcm90ZWlucyBPTkxZIGZvciB0aGUgY29tcGxleGVzIHdlIGhhdmUgaW4gb3VyIGFuYWx5c2lzLiAKICB1bmdyb3VwKCkgJT4lIAogIGdyb3VwX2J5KGBDb21wbGV4IE5hbWVgKSAlPiUgCiAgbXV0YXRlKG5fbWVhbiA9IG1lYW4obl9vdmVybGFwKSkgJT4lIAogIHVuZ3JvdXAoKQoKI3JlbWFpbmluZyBjb21wbGV4ZXMgdG8gYW5hbHl6ZQpjb21wbGV4ZXNfdG9fYW5hbHl6ZSA8LSBjb21wbGV4X2dlbmVzICU+JSAKICBzZWxlY3QoIGBDb21wbGV4IE5hbWVgLCBuX2NvbXBsZXgsIG5fbWVhbikgJT4lIAogIGRpc3RpbmN0KCkKCmNvbXBsZXhfcHJvdF9jb3JfZGYgPC0gY29tcGxleF9wcm90X2NvciAlPiUgCiAgZW5mcmFtZSggIkNvbXBsZXggTmFtZSIsICJkYXRhIikgJT4lIAogIHVubmVzdCgiZGF0YSIpICU+JSAKICBmaWx0ZXIoICFpcy5uYShwcm90ZWluX2lkX2NvbCksICFpcy5uYShwcm90ZWluX2lkX3JvdykgKSAlPiUgIyBmaWx0ZXIgTkFzIGlmIGFueS4KICBmaWx0ZXIoIGBDb21wbGV4IE5hbWVgICVpbiUgY29tcGxleGVzX3RvX2FuYWx5emUkYENvbXBsZXggTmFtZWApICU+JSAjIGZpbHRlciB0aGUgY29tcGxleGVzCiAgZmlsdGVyKCBwcm90ZWluX2lkX2NvbCAlaW4lIGNvbXBsZXhfZ2VuZXMkcHJvdGVpbl9pZCwKICAgICAgICAgIHByb3RlaW5faWRfcm93ICVpbiUgY29tcGxleF9nZW5lcyRwcm90ZWluX2lkKSAjIGZpbHRlciBmb3IgdGhlIGdlbmVzIHRvIGFuYWx5emUKICAKY29tcGxleF9wcm90X2Nvcl9kZiAlPiUgCiAgZmlsdGVyKCBwcm90ZWluX2lkX2NvbCAhPSBwcm90ZWluX2lkX3JvdykgJT4lIAogIGdyb3VwX2J5KGBDb21wbGV4IE5hbWVgKSAlPiUgCiAgc3VtbWFyaXNlKCBtZWRpYW5fcHJvdCA9IG1lZGlhbihjb3JfcHJvdCwgbmEucm09VFJVRSkKICAgICAgICAgICAgICkgJT4lIAogIHVuZ3JvdXAoKSAtPiBtZWFuX2NvbXBsZXhfcHJvdCAKCmNvbXBsZXhfcHJvdF9jb3JfZGYgJT4lIAogIGZpbHRlciggcHJvdGVpbl9pZF9jb2wgIT0gcHJvdGVpbl9pZF9yb3cpICU+JSAKICBncm91cF9ieShgQ29tcGxleCBOYW1lYCxwcm90ZWluX2lkX2NvbCkgJT4lIAogIHN1bW1hcml6ZSggY29yX3Byb3QgPSBtZWRpYW4oY29yX3Byb3QsIG5hLnJtPVQpKSAlPiUgCiAgcmVuYW1lKHByb3RlaW5faWQgPSBwcm90ZWluX2lkX2NvbCkgJT4lIAogIHVuZ3JvdXAoKSAtPiBtZWFuX2dlbmVfY29tcGxleF9wcm90CgoKY29tcGxleF9nZW5lcyAgJT4lIAogIGxlZnRfam9pbiggLiwKICAgICAgICAgICAgIHJlbmFtZShtZWFuX2dlbmVfY29tcGxleF9wcm90LAogICAgICAgICAgICAgICAgICAgIGNvcl9nZW5lX3Byb3QgPSBjb3JfcHJvdCkgKSAlPiUgCiAgbGVmdF9qb2luKC4sIAogICAgICAgICAgICByZW5hbWUobWVhbl9jb21wbGV4X3Byb3QsIAogICAgICAgICAgICAgICAgICAgY29yX2NvbXBsZXhfcHJvdCA9IG1lZGlhbl9wcm90KSktPiBjb21wbGV4X2dlbmVzX2Fubm90YXRlZAoKCmNvbXBsZXhlc19hbm5vdGF0ZWQgPC0gbWVhbl9jb21wbGV4X3Byb3QgJT4lIAogIG11dGF0ZSggY29tcGxleF9xNzUgPSBxdWFudGlsZSggKG1lZGlhbl9wcm90KSwgMC45MCkgLAogICAgICAgICAgY29tcGxleF9xMjUgPSBxdWFudGlsZSggKG1lZGlhbl9wcm90KSwgMC4xKSApICU+JSAKICBtdXRhdGUoIGNvbXBsZXhfdHlwZSA9IGNhc2Vfd2hlbiggbWVkaWFuX3Byb3QgPiBjb21wbGV4X3E3NSB+ICJzdGFibGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZWRpYW5fcHJvdCA8IGNvbXBsZXhfcTI1IH4gInZhcmlhYmxlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKG1lZGlhbl9wcm90ID49IGNvbXBsZXhfcTI1ICYgbWVkaWFuX3Byb3QgPD0gY29tcGxleF9xNzUpIH4gIm5vbmUiKQogICAgICAgICAgKSAgJT4lIAogIHNlbGVjdCggYENvbXBsZXggTmFtZWAsY29tcGxleF90eXBlKSU+JSAKICBsZWZ0X2pvaW4oIHNlbGVjdCggY29tcGxleF9nZW5lc19hbm5vdGF0ZWQsIAogICAgICAgICAgICAgICAgICAgICBgQ29tcGxleCBOYW1lYCwgCiAgICAgICAgICAgICAgICAgICAgIGNvcl9jb21wbGV4X3Byb3QsIAogICAgICAgICAgICAgICAgICAgICBuX2NvbXBsZXgsCiAgICAgICAgICAgICAgICAgICAgIG5fbWVhbikpIAoKRmlndXJlMl9kYXRhIDwtIGNvbXBsZXhfZ2VuZXNfYW5ub3RhdGVkICU+JSAKICBsZWZ0X2pvaW4oIHNlbGVjdChjb21wbGV4ZXNfYW5ub3RhdGVkLCBgQ29tcGxleCBOYW1lYCwgY29tcGxleF90eXBlKSkgJT4lIAogIGRpc3RpbmN0KCkgJT4lIAogIG11dGF0ZSggbWdpX3N5bWJvbCA9IHRvdXBwZXIobWdpX3N5bWJvbCkpICU+JSAKICBzZWxlY3QoIGBDb21wbGV4IE5hbWVgLCAKICAgICAgICAgIGBDb21wbGV4IENvaGVzaXZlbmVzYCA9IGNvcl9jb21wbGV4X3Byb3QsIAogICAgICAgICAgYFByb3RlaW5gID0gbWdpX3N5bWJvbCwKICAgICAgICAgIGBBdmVyYWdlIHBhaXJ3aXNlIGNvcnJlbGF0aW9uYCA9IGNvcl9nZW5lX3Byb3QpICU+JSAKICBkaXN0aW5jdCgpCgpgYGAKCgoKYGBge3IgRmlndXJlMiwgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9MTUsIGZpZy5jYXA9IkZpZ3VyZSAyOiBGb3IgZWFjaCBjb21wbGV4LCBwYWlyd2lzZSBQZWFyc29uIGNvcnJlbGF0aW9ucyB3ZXJlIGNhbGN1bGF0ZWQgYmV0d2VlbiBhbGwgcHJvdGVpbiBzdWJ1bml0cyBhbmQgc3VtbWFyaXplZCBhcyBhIGJveHBsb3QuIEJveHBsb3RzIGFyZSBvcmRlcmVkIGFuZCBjb2xvcmVkIGJhc2VkIG9uIHRoZWlyIG1lZGlhbiBwYWlyd2lzZSBjb3JyZWxhdGlvbiwgd2l0aCBtb3JlIGNvaGVzaXZlIGNvbXBsZXhlcyBvbiB0aGUgbGVmdC4gU3BlY2lmaWMgZXhhbXBsZXMgb2YgdGhlIHN0YWJsZSAobW9zdCBjb2hlc2l2ZSAxMCUpIGFuZCB2YXJpYWJsZSAobGVhc3QgY29oZXNpdmUgMTAlKSBjb21wbGV4ZXMgYXJlIGhpZ2hsaWdodGVkIn0KCkZpZ3VyZTJfZGF0YSAlPiUgCiAgYXJyYW5nZSggZGVzYyhgQ29tcGxleCBDb2hlc2l2ZW5lc2ApICkgJT4lIAogIG11dGF0ZSggbGFiZWwgPSBmYWN0b3IoYENvbXBsZXggTmFtZWAsIGxldmVscyA9IHVuaXF1ZShgQ29tcGxleCBOYW1lYCkpKSAlPiUgCiAgZ2dib3hwbG90KAogICAgeCA9ICJsYWJlbCIsCiAgICB5ID0gIkF2ZXJhZ2UgcGFpcndpc2UgY29ycmVsYXRpb24iLAogICAgZmlsbCA9ICJDb21wbGV4IENvaGVzaXZlbmVzIiwKICAgIHNvcnQudmFsID0gImRlc2MiLAogICAgeGxhYiA9ICIiLAogICAgeWxhYiA9ICJDb3JyZWxhdGlvbiIsCiAgICB3aWR0aCA9IDEKICApKwogIHRoZW1lX3B1YmNsZWFuKCBiYXNlX3NpemUgPSAxMCwgYmFzZV9mYW1pbHkgPSJwb3BwaW5zIikrCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSAwLCBoanVzdCA9IDEsIHNpemUgPSAwKSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAidG9wIiwKICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChhbmdsZT0xNSwgaGp1c3QgPTAuMSwgc2l6ZSA9IDEyKSwKICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoYW5nbGU9MCwgaGp1c3QgPTAsIHZqdXN0ID0gMC45LCBzaXplID0gMTYpLAogICAgICAgIGxlZ2VuZC50aXRsZS5hbGlnbiA9IDEsCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoYW5nbGU9MCwgc2l6ZSA9IDE4KSwKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoYW5nbGU9OTAsIHNpemUgPSAxOCksCiAgICAgICAgYXhpcy50aWNrcy54ID0gZWxlbWVudF9ibGFuaygpKSsKICBsYWJzKCBmaWxsID0gIkNvbXBsZXhcbmNvaGVzaXZlbmVzcyIpKwogIHNjYWxlX2ZpbGxfdmlyaWRpc19jKCkKCmBgYAoKPGJyPgoKYGBge3IgRmlndXJlMl9kYXRhLCBmaWcuY2FwPSJEYXRhIHVzZWQgdG8gZ2VuZXJhdGUgRmlndXJlIDIuIn0KCiBGaWd1cmUyX2RhdGEgJT4lIAogIG11dGF0ZV9pZiggaXMubnVtZXJpYywgcm91bmQgLDIgKSAlPiUgCiAgY3JlYXRlX2R0KCkKICAKCmBgYAoKPGJyPgoKIyMjIyBGaWd1cmUgUzJBOiBWYXJpYXRpb24gaW4gY29tcGxleCBmb3JtaW5nIGFuZCBvdGhlciBwcm90ZWlucwoKCmBgYHtyIEZpZ3VyZV9TMkFCLCBmaWcuY2FwID0gIkZpZ3VyZSBTMkE6IFByb3RlaW5zIHRoYXQgYXJlIHBhcnQgb2YgYSBjb21wbGV4IHNob3cgbGVzcyB2YXJpYXRpb24uIEJveHBsb3RzIGRlcGljdGluZyAlIGNvZWZmaWNpZW50IG9mIHZhcmlhdGlvbiBvZiBwcm90ZWluIGFidW5kYW5jZSBmb3IgZ2VuZXMgdGhhdCBhcmUgY29tcGxleCBtZW1iZXJzIGFuZCBub3QgY29tcGxleCBtZW1iZXJzLiIsIGZpZy53aWR0aD0zLCBmaWcuaGVpZ2h0PTR9CgojIENvZGUgdXNlZCB0byBnZW5lcmF0ZSBGaWd1cmUgczJBCnZhcl9tZWFuX3Byb3QgJT4lIAogIG11dGF0ZShzZCA9IHNxcnQodmFyKSkgJT4lCiAgbXV0YXRlKGN2LnByb3QgPSAxMDAgKiBzZCAvIChtZWFuKSkgJT4lCiAgcmVuYW1lKG1lYW4ucHJvdCA9IG1lYW4sIHNkLnByb3QgPSBzZCkgJT4lIAogIG11dGF0ZShjb21wbGV4X21lbWJlciA9IGlmZWxzZShwcm90ZWluX2lkICVpbiUgY29tcGxleC5nZW5lLmxpc3QkcHJvdGVpbl9pZCwgVFJVRSwgRkFMU0UpKSAlPiUKICBnZ2JveHBsb3QoCiAgICB4ID0gImNvbXBsZXhfbWVtYmVyIiwKICAgIHkgPSAiY3YucHJvdCIsIGNvbCA9ICJjb21wbGV4X21lbWJlciIsICBwYWxldHRlID0gYygiZ3JheSIsInJlZCIpLCBsZWdlbmQgPSAibm9uZSIsIHdpZHRoID0gMC4yCiAgKSArCiAgc3RhdF9jb21wYXJlX21lYW5zKG1ldGhvZCA9ICJhbm92YSIsIGxhYmVsLnkgPSAxLjgpICsKICB5bGFiKCIlIENvZWZmaWNpZW50IG9mIHZhcmlhdGlvbiIpICsgdGhlbWVfcHViY2xlYW4oYmFzZV9zaXplID0gMTIpICsgc2NhbGVfeV9sb2cxMCgpICsgeGxhYigiQ29tcGxleCBtZW1iZXIiKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSAtPiBmaWdfczJhCgpnZ2FycmFuZ2UoZmlnX3MyYSwgCiAgICAgICAgICBsYWJlbHMgPSAiQSIsIAogICAgICAgICAgZm9udC5sYWJlbCA9IGxpc3QoIHNpemUgPSAyMCkpCgoKYGBgCgo8YnI+CgojIyMjIEZpZ3VyZSBTMkI6IFByb3RlaW4gY28tYWJ1bmRhbmNlIGluIERPIG1FU0NzIGluIGNvbXBsZXggZm9ybWluZyBhbmQgb3RoZXIgcHJvdGVpbnMKCmBgYHtyIEZpZ3VyZVMyQiwgZmlnLmNhcD0iRmlndXJlIFMyQjogUHJvdGVpbnMgdGhhdCBwaHlzaWNhbGx5IGludGVyYWN0IHNob3cgaGlnaGVyIHBhaXJ3aXNlIGNvcnJlbGF0aW9uIGluIGFidW5kYW5jZSB0aGFuIG5vbi1pbnRlcmFjdGluZyBwcm90ZWlucy4gRGVuc2l0eSBkaXN0cmlidXRpb25zIG9mIHBhaXJ3aXNlIFBlYXJzb24gY29ycmVsYXRpb25zIGJldHdlZW4gY29tcGxleCBmb3JtaW5nIHByb3RlaW5zIGFuZCBvdGhlcnMgYXJlIHBsb3R0ZWQuIiwgY2FjaGUgPSBUUlVFLCBmaWcuaGVpZ2h0PTQsIGZpZy53aWR0aD02fQoKIyBDb2RlIHVzZWQgdG8gZ2VuZXJhdGUgRmlndXJlIFMyQgoKIyBjb21wbGV4IHZzIG90aGVycwpub24uY29tcGxleC5wcm90LmNvciA8LSByY29ycihleHByLmVzY19wcm90WywgKGZpbHRlcihhbGwucHJvdHMsICFwcm90ZWluX2lkICVpbiUgY29tcGxleC5nZW5lLmxpc3QkcHJvdGVpbl9pZCkpJHByb3RlaW5faWRdKQpkaWFnKG5vbi5jb21wbGV4LnByb3QuY29yJHIpIDwtIE5BCgpjb21wbGV4X3Byb3RfY29yX2RmICU+JQogIHNlbGVjdCggLWVuc2VtYmxfZ2VuZV9pZF9jb2wsIC1lbnNlbWJsX2dlbmVfaWRfcm93KSAlPiUKICBkaXN0aW5jdCgpICU+JQogIGZpbHRlciggcHJvdGVpbl9pZF9jb2wgIT0gcHJvdGVpbl9pZF9yb3cpIC0+IGRhdC5wcm90Cgp0aWJibGUoY29yID0gKGMobm9uLmNvbXBsZXgucHJvdC5jb3IkcikpKSAlPiUKICBnZ3Bsb3QoKSsKICBnZW9tX2RlbnNpdHkoIGFlcyh4PWNvciwKICAgICAgICAgICAgICAgICAgICBmaWxsID0gIk90aGVyIHBhaXJzIiksCiAgICAgICAgICAgICAgICBhbHBoYSA9MC41KSAtPiBwMQoKcDErZ2VvbV9kZW5zaXR5KGRhdGE9IGRhdC5wcm90LAogICAgICAgICAgICAgICBhZXMoeD1jb3JfcHJvdCwKICAgICAgICAgICAgICAgICAgICBmaWxsID0gIkNvbXBsZXggZm9ybWluZyBwYWlycyIgKSwKICAgICAgICAgICAgICAgIGFscGhhID0gMC41KSsKICBzY2FsZV9maWxsX21hbnVhbCggdmFsdWVzID0gYygiQ29tcGxleCBmb3JtaW5nIHBhaXJzIj0icmVkIiwKICAgICAgICAgICAgICAgICAgICAgICAgIk90aGVyIHBhaXJzIj0iZ3JheSIpKSsKICB0aGVtZV9wdWJjbGVhbihiYXNlX3NpemUgPSAxNCkrCiAgeGxhYigiQ29ycmVsYXRpb24iKSsKICBsYWJzKGZpbGw9IiIpIC0+IHBsb3RfcHJvdGVpbl9jb2FiCgpnZ2FycmFuZ2UocGxvdF9wcm90ZWluX2NvYWIsIAogICAgICAgICAgbGFiZWxzID0gIkIiLAogICAgICAgICAgZm9udC5sYWJlbCA9IGxpc3QoIHNpemUgPSAyMCkpCgpgYGAKCjxicj4KCiMjIyMgRmlndXJlIFMyQzogUHJvdGVpbiBjb21wbGV4ZXMgdGhhdCBzaG93IHNleCBlZmZlY3RzIAoKYGBge3IgRmlndXJlIFMyQywgZmlnLmNhcD0iRmlndXJlIFMyQzogU2V4IGluZmx1ZW5jZXMgdGhlIGNvLXJlZ3VsYXRpb24gb2YgY29tcGxleCBzdWJ1bml0cy4gQm94cGxvdHMgb2YgcGFpcndpc2UgUGVhcnNvbiBjb3JyZWxhdGlvbnMgYW1vbmcgY29tcGxleCBzdWJ1bml0cyB3aXRoIHNpZ25pZmljYW50IGRpZmZlcmVuY2VzIGJldHdlZW4gbWFsZSBhbmQgZmVtYWxlIGNlbGwgbGluZXMgYXJlIHNob3duIChPbmUgd2F5IEFOT1ZBIGZvbGxvd2VkIGJ5IFR1a2V54oCZcyBIU0QsICoqKio6IHAgdmFsdWUgPCAwLjAwMDA1KS4iLCBjYWNoZSA9IFRSVUUsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD00fQoKY29tcGxleF9wcm90X2Nvcl9tYWxlIDwtIGNvbXBsZXguZ2VuZXMgJT4lCiAgbWFwKCBnZXRfY29yciwgc3Vic2V0ID0iTSIpIAoKY29tcGxleF9wcm90X2Nvcl9mZW1hbGUgPC0gY29tcGxleC5nZW5lcyAlPiUKICBtYXAoIGdldF9jb3JyLCBzdWJzZXQgPSJGIikgCgoKY29tcGxleF9wcm90X2Nvcl9mZW1hbGUgJT4lCiAgZW5mcmFtZSggIkNvbXBsZXggTmFtZSIsICJkYXRhIikgJT4lIAogIHVubmVzdCgiZGF0YSIpICU+JSAKICBmaWx0ZXIoICFpcy5uYShwcm90ZWluX2lkX2NvbCksICFpcy5uYShwcm90ZWluX2lkX3JvdykgKSAlPiUgIyBmaWx0ZXIgTkFzIGlmIGFueS4KICBmaWx0ZXIoIGBDb21wbGV4IE5hbWVgICVpbiUgY29tcGxleGVzX3RvX2FuYWx5emUkYENvbXBsZXggTmFtZWApICU+JSAjIGZpbHRlciB0aGUgY29tcGxleGVzCiAgZmlsdGVyKCBwcm90ZWluX2lkX2NvbCAlaW4lIGNvbXBsZXhfZ2VuZXMkcHJvdGVpbl9pZCwKICAgICAgICAgIHByb3RlaW5faWRfcm93ICVpbiUgY29tcGxleF9nZW5lcyRwcm90ZWluX2lkKSAlPiUgCiAgZ3JvdXBfYnkoYENvbXBsZXggTmFtZWApICU+JSAKICBtdXRhdGUobl9jb21wbGV4ID1uX2Rpc3RpbmN0KHByb3RlaW5faWRfY29sKSApICU+JQogIGZpbHRlcihuX2NvbXBsZXggPiA0KSAlPiUgCiAgbXV0YXRlKCBzZXggPSAiRiIpIC0+IGNvbXBsZXhfcHJvdF9jb3JfZmVtYWxlX2RmCgpjb21wbGV4X3Byb3RfY29yX21hbGUgJT4lIAogIGVuZnJhbWUoICJDb21wbGV4IE5hbWUiLCAiZGF0YSIpICU+JSAKICB1bm5lc3QoImRhdGEiKSAlPiUgCiAgZmlsdGVyKCAhaXMubmEocHJvdGVpbl9pZF9jb2wpLCAhaXMubmEocHJvdGVpbl9pZF9yb3cpICkgJT4lICMgZmlsdGVyIE5BcyBpZiBhbnkuCiAgZmlsdGVyKCBgQ29tcGxleCBOYW1lYCAlaW4lIGNvbXBsZXhlc190b19hbmFseXplJGBDb21wbGV4IE5hbWVgKSAlPiUgIyBmaWx0ZXIgdGhlIGNvbXBsZXhlcwogICBmaWx0ZXIoIHByb3RlaW5faWRfY29sICVpbiUgY29tcGxleF9nZW5lcyRwcm90ZWluX2lkLAogICAgICAgICAgcHJvdGVpbl9pZF9yb3cgJWluJSBjb21wbGV4X2dlbmVzJHByb3RlaW5faWQpICU+JSAKICBncm91cF9ieShgQ29tcGxleCBOYW1lYCkgJT4lIAogIG11dGF0ZShuX2NvbXBsZXggPW5fZGlzdGluY3QocHJvdGVpbl9pZF9jb2wpICkgJT4lCiAgZmlsdGVyKG5fY29tcGxleCA+IDQpICU+JSAKICBtdXRhdGUoc2V4ID0iTSIpIC0+IGNvbXBsZXhfcHJvdF9jb3JfbWFsZV9kZgoKY29tcGxleF9wcm90X2Nvcl9zZXhlcyA8LSByYmluZCggY29tcGxleF9wcm90X2Nvcl9mZW1hbGVfZGYsIGNvbXBsZXhfcHJvdF9jb3JfbWFsZV9kZikgJT4lIAogIHBpdm90X3dpZGVyKCBuYW1lc19mcm9tID0gc2V4LCB2YWx1ZXNfZnJvbSA9IGMoImNvcl9wcm90Iiwibl9wcm90IiwicF9wcm90IikpCgojIGFub3ZhIGZpcnN0CiMgZm9sbG93IHVwIG9uIHRoZSBvbmVzIHdpdGggc2lnbmlmaWNhbmNlIApyYmluZCggY29tcGxleF9wcm90X2Nvcl9mZW1hbGVfZGYsIGNvbXBsZXhfcHJvdF9jb3JfbWFsZV9kZikgJT4lIAogIGdyb3VwX2J5KGBDb21wbGV4IE5hbWVgKSAlPiUgCiAgcnN0YXRpeDo6YW5vdmFfdGVzdCggY29yX3Byb3QgfiBzZXgpICAlPiUgIyBkZWZhdWx0IGlzIHR3by5zaWRlZC4KICByc3RhdGl4OjphZGp1c3RfcHZhbHVlKCBtZXRob2QgPSAiQkgiICkgJT4lCiAgcnN0YXRpeDo6YWRkX3NpZ25pZmljYW5jZSgicC5hZGoiKSAlPiUgCiAgYXNfdGliYmxlKCkgLT4gY29tcGxleF9hb3ZfcmVzdWx0cwoKIyBwYXNzaW5nIHRoZSBmdWxsIGRhdGEgKyBkb2luZyB0aGUgY29ycmVjdGlvbiB0aGVuIGZpbHRlcmluZwpyYmluZCggY29tcGxleF9wcm90X2Nvcl9mZW1hbGVfZGYsIGNvbXBsZXhfcHJvdF9jb3JfbWFsZV9kZikgJT4lIAogIGdyb3VwX2J5KGBDb21wbGV4IE5hbWVgKSAlPiUgCiAgIyByc3RhdGl4Ojp0X3Rlc3QoIGNvcl9wcm90IH4gc2V4LCBwYWlyZWQgPSBUUlVFKSAlPiUgCiAgIyByc3RhdGl4OjphZGp1c3RfcHZhbHVlKCBtZXRob2QgPSAiQkgiICkgJT4lCiAgcnN0YXRpeDo6dHVrZXlfaHNkKCBjb3JfcHJvdCB+c2V4KSAlPiUgCiAgcnN0YXRpeDo6YWRkX3NpZ25pZmljYW5jZSgicC5hZGoiKSAlPiUgCiAgZmlsdGVyKGBDb21wbGV4IE5hbWVgICVpbiUgKGZpbHRlcihjb21wbGV4X2Fvdl9yZXN1bHRzLCBwLmFkai5zaWduaWYgIT0gIm5zIikpJGBDb21wbGV4IE5hbWVgLAogICAgICAgICBwLmFkai5zaWduaWYhPSAibnMiKSAtPiBjb21wbGV4X3R1a2V5c19yZXN1bHRzCgoKcmJpbmQoIGNvbXBsZXhfcHJvdF9jb3JfZmVtYWxlX2RmLCBjb21wbGV4X3Byb3RfY29yX21hbGVfZGYpICU+JSAKICBmaWx0ZXIoYENvbXBsZXggTmFtZWAgJWluJSBmaWx0ZXIoY29tcGxleF90dWtleXNfcmVzdWx0cywgcC5hZGouc2lnbmlmICE9ICJucyIpJGBDb21wbGV4IE5hbWVgKSAlPiUgCiAgZ2dib3hwbG90KAogICAgeCA9ICJzZXgiLAogICAgeSA9ICJjb3JfcHJvdCIsCiAgICBjb2wgPSAic2V4IiwKICAgIHBhbGV0dGUgPSAibnBnIiwKICAgICNmYWNldC5ieSA9ICJgQ29tcGxleCBOYW1lYCIsCiAgICB5bGFiID0gIkNvcnJlbGF0aW9uIiwKICAgIGdndGhlbWUgPSB0aGVtZV9wdWJjbGVhbihiYXNlX3NpemUgPSAxOCwgYmFzZV9mYW1pbHkgPSAiUG9wcGlucyIpLAogICAgd2lkdGggPSAwLjIKICApKwogIHN0YXRfcHZhbHVlX21hbnVhbCggZGF0YSA9IGZpbHRlcihjb21wbGV4X3R1a2V5c19yZXN1bHRzLCBwLmFkai5zaWduaWYgIT0gIm5zIiksCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbCA9ICJwLmFkai5zaWduaWYiLAogICAgICAgICAgICAgICAgICAgICAgeS5wb3NpdGlvbiA9IDEuMSkrCiAgeWxpbSgtMC41LCAxLjIpIC0+IHBfY29tcGxleF9zZXgKCmZpZ19zMmMgPC0gZmFjZXQoIHBfY29tcGxleF9zZXgsIAogICAgICAgZmFjZXQuYnkgPSAiYENvbXBsZXggTmFtZWAiLCAKICAgICAgIHBhbmVsLmxhYnMuZm9udCA9IGxpc3QoIHNpemUgPSAxMCksCiAgICAgICBucm93ID0gMSkKCmdnYXJyYW5nZShmaWdfczJjLCAKICAgICAgICAgIGxhYmVscyA9ICJDIiwKICAgICAgICAgIGZvbnQubGFiZWwgPSBsaXN0KCBzaXplID0gMjIpKQoKYGBgCgoKPGJyPgoKPCEtLSAjIyMjIEZpZ3VyZSBTMkQ6IENvbXBsZXggbWVtYmVyc2hpcCBpbiBtb3N0IHN0YWJsZSBhbmQgdmFyaWFibGUgY29tcGxleGVzIC0tPgoKPCEtLSBgYGB7ciBGaWd1cmVTMkQsIGZpZy5jYXA9IkZpZ3VyZSBTMkQ6IFZhcmlhYmxlIGNvbXBsZXhlcyBhcmUgbW9yZSBsaWtlbHkgdG8gaGF2ZSBwcm9taXNjdW91cyBzdWJ1bml0cyB0aGF0IGFyZSBwYXJ0IG9mIG1vcmUgdGhhbiAyIGNvbXBsZXhlcy4gUGFpcndpc2UgUGVhcnNvbiBjb3JyZWxhdGlvbiBjb2VmZmljaWVudHMgcGxvdHRlZCBmb3IgYWxsIHN1YnVuaXRzIHRoYXQgYXJlIHBhcnQgb2Ygc3RhYmxlICh1cHBlciAxMHRoIHBlcmNlbnRpbGUsIG1vc3QgY29oZXNpdmUpIGFuZCB2YXJpYWJsZSAobG93ZXIgMTB0aCBwZXJjZW50aWxlLCBsZWFzdCBjb2hlc2l2ZSkgY29tcGxleGVzIHdoZXJlIHRoZSBwcm90ZWlucyBhcmUgY29sb3JlZCBieSB0aGUgbnVtYmVyIG9mIGNvbXBsZXhlcyB0aGV5IGJlbG9uZyB0by4iLCBmaWcud2lkdGg9MTYsIGZpZy5oZWlnaHQ9OH0gLS0+Cgo8IS0tIGNvbXBsZXhlc19hbm5vdGF0ZWQgJT4lICAtLT4KPCEtLSAgIGZpbHRlciggY29tcGxleF90eXBlICE9ICJub25lIikgJT4lICAtLT4KPCEtLSAgIGxlZnRfam9pbiggc2VsZWN0KGNvbXBsZXhfcHJvdF9jb3JfZGYsIGNvcl9wcm90LCBwcm90ZWluX2lkID0gcHJvdGVpbl9pZF9yb3csIGBDb21wbGV4IE5hbWVgKSkgJT4lICAtLT4KPCEtLSAgIGxlZnRfam9pbihzZWxlY3QoY29tcGxleF9nZW5lc19hbm5vdGF0ZWQsIGBDb21wbGV4IE5hbWVgLCBwcm90ZWluX2lkLCBuX292ZXJsYXAsIGNvcl9jb21wbGV4X3Byb3QpKSAlPiUgIC0tPgo8IS0tICAgYXJyYW5nZSggZGVzYyhjb3JfY29tcGxleF9wcm90KSApJT4lICAtLT4KPCEtLSAgIG11dGF0ZSggbGFiZWwgPSBmYWN0b3IoYENvbXBsZXggTmFtZWAsIGxldmVscyA9IHVuaXF1ZShgQ29tcGxleCBOYW1lYCkpKSAlPiUgIC0tPgo8IS0tICAgZ2dwbG90KCkrIC0tPgo8IS0tICAgYWVzKHkgPSBsYWJlbCwgLS0+CjwhLS0gICAgICAgeCA9IGNvcl9wcm90LCAtLT4KPCEtLSAgICAgICBjb2wgPSBhc19mYWN0b3Iobl9vdmVybGFwKSkrIC0tPgo8IS0tICAgZ2VvbV9wb2ludChzaXplID0gMywgYWxwaGEgPSAwLjYpKyAtLT4KPCEtLSAgIHRoZW1lX3B1YmNsZWFuKDE4KSsgLS0+CjwhLS0gICBzY2FsZV9jb2xvcl92aXJpZGlzX2QoIG9wdGlvbiA9ICJDIikrIC0tPgo8IS0tICAgdGhlbWUoIC0tPgo8IS0tICAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoYW5nbGU9MCwgdmp1c3Q9MCwgc2l6ZSA9MTQpKSsgLS0+CjwhLS0gICB5bGFiKCIiKSsgLS0+CjwhLS0gICB4bGFiKCJDb3JyZWxhdGlvbiIpKyAtLT4KPCEtLSAgIHhsaW0oYygtMSwxKSkrIC0tPgo8IS0tICAgbGFicyhjb2wgPSAiQ29tcGxleCBtZW1iZXJzaGlwIikrIC0tPgo8IS0tICAgZmFjZXRfd3JhcCh+Y29tcGxleF90eXBlLCBuY29sID0gMiwgbnJvdyA9IDEsIHNjYWxlcz0gImZyZWUiKSAtPiBmaWd1cmVfczJkIC0tPgoKPCEtLSBnZ2FycmFuZ2UoZmlndXJlX3MyZCwgIC0tPgo8IS0tICAgICAgICAgICBsYWJlbHMgPSAiRCIsIC0tPgo8IS0tICAgICAgICAgICBmb250LmxhYmVsID0gbGlzdCggc2l6ZSA9IDI0KSkgLS0+CgoKPCEtLSBgYGAgLS0+CgoKPCEtLSA8YnI+IC0tPgoKPCEtLSAjIyMjIEZpZ3VyZSBTMkU6IFByb3RlaW5zIHRoYXQgYmVsb25nIHRvIG11bHRpcGxlIGNvbXBsZXhlcyBzaG93IHZhcmlhYmxlIGJlaGF2aW91ciBpbiBjb21wbGV4IHByZWZlcmVuY2UgLS0+Cgo8IS0tIGBgYHtyLCBGaWd1cmVTMkUsIGZpZy5jYXA9IkZpZ3VyZSBTMkU6IFByb21pc2N1b3VzIHByb3RlaW5zIHZhcnkgaW4gcHJlZmVyZW5jZSBvZiBjb21wbGV4ZXMuIEJveHBsb3RzIG9mIG1lZGlhbiBwYWlyd2lzZSBQZWFyc29uIGNvcnJlbGF0aW9ucyBvZiBjb21wbGV4IHN1YnVuaXRzIGFjcm9zcyB2YXJpb3VzIGNvbXBsZXhlcyB0aGV5IGFyZSBwYXJ0IG9mIGFyZSBwbG90dGVkLiBUaGUgY29tcGxleCBzdWJ1bml0cyBhcmUgc2VwYXJhdGVkIGludG8gdHdvIGNhdGVnb3JpZXMgYmFzZWQgb24gdGhlIHRvdGFsIG51bWJlciBvZiBjb21wbGV4ZXMgdGhleSBiZWxvbmcgdG8uIiwgZmlnLmhlaWdodD03LCBmaWcud2lkdGg9MTB9IC0tPgoKPCEtLSBwcm9tc2NzX2dlbmVzIDwtIGNvbXBsZXhfZ2VuZXNfYW5ub3RhdGVkICU+JSAgLS0+CjwhLS0gICBmaWx0ZXIoICNwcm9taXNjcz09VFJVRSwgLS0+CjwhLS0gICAgICAgICAgIG5fb3ZlcmxhcCA+IDIpICU+JSAgLS0+CjwhLS0gICBzZWxlY3QoIHByb3RlaW5faWQpICU+JSAgLS0+CjwhLS0gICBkaXN0aW5jdCgpICAtLT4KCjwhLS0gY29tcGxleF9nZW5lc19hbm5vdGF0ZWQgJT4lICAtLT4KPCEtLSAgIGZpbHRlciggcHJvdGVpbl9pZCAlaW4lICBwcm9tc2NzX2dlbmVzJHByb3RlaW5faWQpICU+JSAgLS0+CjwhLS0gICBncm91cF9ieShwcm90ZWluX2lkKSAlPiUgIC0tPgo8IS0tICAgI2ZpbHRlciggbl9kaXN0aW5jdChgQ29tcGxleCBOYW1lYCkgPiAyKSAlPiUgIC0tPgo8IS0tICAgbXV0YXRlKCBtZWFuX3Byb3QgPSBtZWFuKGNvcl9nZW5lX3Byb3QsIG5hLnJtPVQpLCAtLT4KPCEtLSAgICAgICAgICAgc2RfcHJvdCA9IHNxcnQodmFyKGNvcl9nZW5lX3Byb3QsIG5hLnJtPVQpKSwgLS0+CjwhLS0gICAgICAgICAgIGN2X3Byb3QgPSBhYnMoc2RfcHJvdC9tZWFuX3Byb3QpICwgLS0+CjwhLS0gICAgICAgICAgIG5fY29tcGxleCA9IG4oKSkgJT4lICAtLT4KPCEtLSAgIHVuZ3JvdXAoKSAlPiUgIC0tPgo8IS0tICAgZmlsdGVyKCFpcy5uYShzZF9wcm90KSwgbl9jb21wbGV4ID4gMiktPiBzaGFyZWRfY29tcGxleF9wcm90cyAtLT4KCgo8IS0tIHNoYXJlZF9jb21wbGV4X3Byb3RzICU+JSAgLS0+CjwhLS0gICAjZmlsdGVyKCAhcHJvdGVpbl9pZCAlaW4lIGxvd19jb3JfZ2VuZXMkcHJvdGVpbl9pZCApICU+JSAgLS0+CjwhLS0gICBtdXRhdGUoIHNpemUgPSBpZmVsc2UoIG5fb3ZlcmxhcCA+IDMsICJDb21wbGV4IG1lbWJlcnNoaXAgPjMiLCJDb21wbGV4IG1lbWJlcnNoaXAgPSAzIiApKSAlPiUgIC0tPgo8IS0tICAgZ3JvdXBfYnkobWdpX3N5bWJvbCkgJT4lIC0tPgo8IS0tICAgbXV0YXRlKG5ld19zeW1ib2wgPSBpZmVsc2UoIHByb3RlaW5faWQgPT0iRU5TTVVTUDAwMDAwMTMwNjExIiwgIkFDVEItMjA4IiwgLS0+CjwhLS0gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKCBwcm90ZWluX2lkID09ICJFTlNNVVNQMDAwMDAwOTgwNjYiLCAiQUNUQi0yMDEiLCB0b3VwcGVyKG1naV9zeW1ib2wpICkpKSAlPiUgIC0tPgo8IS0tICAgYXJyYW5nZSggc2RfcHJvdCkgJT4lICAtLT4KPCEtLSAgIG11dGF0ZSggbGFiZWwgPSBmYWN0b3IobmV3X3N5bWJvbCAsIGxldmVscyA9IHVuaXF1ZShuZXdfc3ltYm9sKSkgKSAlPiUgIC0tPgo8IS0tICAgZ2dwbG90KCkrIC0tPgo8IS0tICAgYWVzKCB4ID0gY29yX2dlbmVfcHJvdCwgLS0+CjwhLS0gICAgICAgIHkgPSByZW9yZGVyKGxhYmVsLCAtc2RfcHJvdCksIC0tPgo8IS0tICAgICAgIGNvbCA9IHNkX3Byb3QgLS0+CjwhLS0gICAgICAgICkrIC0tPgo8IS0tICAgZ2VvbV9wb2ludCggc2l6ZSA9IDQsIGFscGhhID0gMSkrIC0tPgo8IS0tICAgIyBnZW9tX2JlZXN3YXJtKCBzaXplID0gMywgYWxwaGE9MSwgZ3JvdXBPblggPSBGKSsgLS0+CjwhLS0gICAjIGdlb21fYm94cGxvdCggYWxwaGEgPTAuOCwgLS0+CjwhLS0gICAjICAgICAgICAgICAgICAgb3V0bGllci5jb2xvdXIgPSAiZGFya2dvbGRlbnJvZDEiLCAtLT4KPCEtLSAgICMgICAgICAgICAgICAgICBvdXRsaWVyLnNpemUgPSAzKSsgLS0+CjwhLS0gICB0aGVtZV9wdWJjbGVhbihiYXNlX3NpemUgPSAxNikrIC0tPgo8IS0tICAgeWxhYigiIikrIC0tPgo8IS0tICAgeGxhYigiTWVkaWFuIGNvcnJlbGF0aW9uIHBlciBnZW5lIHBlciBjb21wbGV4IikrIC0tPgo8IS0tICAgc2NhbGVfY29sb3JfdmlyaWRpcyhvcHRpb24gPSAibWFnbWEiLCBkaXJlY3Rpb24gPSAxKSsgLS0+CjwhLS0gICBsYWJzKGNvbD0iU3RhbmRhcmQgZGV2aWF0aW9uIikrIC0tPgo8IS0tICAgdGhlbWUobGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoYW5nbGU9NDUpKSsgLS0+CjwhLS0gICBmYWNldF93cmFwKH5zaXplLCBzY2FsZXM9ImZyZWVfeSIpIC0+IGZpZ3VyZV9zMmUgLS0+Cgo8IS0tIGdnYXJyYW5nZShmaWd1cmVfczJlLCAgLS0+CjwhLS0gICAgICAgICAgIGxhYmVscyA9ICJFIiwgLS0+CjwhLS0gICAgICAgICAgIGZvbnQubGFiZWwgPSBsaXN0KCBzaXplID0gMjApKSAtLT4KPCEtLSBgYGAgLS0+CgoKCgo=